package algorithm;
/* 本例子是:求出迷宫的最短路径
* 如果是找一点出路而不管远近,则可以用队列来处理,类似二叉树的宽度遍历。
* 在迷宫问题上,类似在一颗4叉树中,利用遍历去找一条从根到某个节点的路径一样。
* 方法为:
* 处理下面的节点,
* 1:从队列中取出最前面的节点,记为K,直到队列里面没有节点为止。
* 2:对它的北节点,南节点,西节点,东节点,记为节点M,逐一按下面处理。
* 2.1:如果M节点未处理,且不是起始点,则设置M节点的路径长度为 K节点的长度+1,把节点M入队列,设置节点M的preNode为K。
* 2.2:如果M节点被处理过,但是它的长度大于(K节点的长度+1),则设置M节点的路径长度为K节点的长度+1,把节点M入队列,设置节点M 的preNode为K。
* 2.3:如果是其它情况,都不处理。
* 3.如果出口节点的长度不为-1,则从出口节点的preNode开始,逐一打印出节点,这些节点就是最短路径上的节点。
*/
import java.util.*;
public class MazeShow {
public static class MazeNode {
/* 0 means it is not processed yet or it is entrance point
* >0 means the shortest distance from the entrance node
*/
private int distance;
/*
* data: it is setup during initialization phase.
* -1: means this node is wall.
* 0: means it can be went through.
*/
private int data;
private int row;
private int column;
boolean isStartPoint;
boolean isExitPoint;
private MazeNode prevNode;
public MazeNode(){
distance=0;
data=0;
column=0;
row=0;
isStartPoint=false;
isExitPoint=false;
prevNode=null;
}
public MazeNode getprevNode(){
return prevNode;
}
public void setprevNode(MazeNode mprevNode){
prevNode=mprevNode;
}
public int getRow(){
return row;
}
public int getColumn(){
return column;
}
public void setRow(int row){
this.row=row;
}
public void setColumn(int column){
this.column=column;
}
public int getDistance(){
return distance;
}
public void setData(int data){
this.data=data;
}
public void setDistance(int dis){
distance=dis;
}
public int getData(){
return data;
}
public void setStartPoint(boolean value){
isStartPoint=value;
}
public void setEndPoint(boolean value){
isExitPoint=value;
}
public boolean getStartPoint(){
return isStartPoint;
}
public boolean getEndPoint(){
return isExitPoint;
}
}
public static int getMazeShortestDistance(){
int MAZE_WIDTH=5,MAZE_HEIGHT=5,distance=0;
MazeShow.MazeNode[][] mymaze=new MazeShow.MazeNode[MAZE_HEIGHT][MAZE_WIDTH];
for(int height=0;height<MAZE_HEIGHT;height++)
for(int width=0;width<MAZE_WIDTH;width++) {
//Java OBJ array must create new each obj element after TYPE[][] m=new TYPE[5]6];
mymaze[height][width]=new MazeShow.MazeNode();
mymaze[height][width].setRow(height);
mymaze[height][width].setColumn(width);
mymaze[height][width].setprevNode(null);
}
//set up as(0,0) as entrance point
mymaze[0][0].setData(0);
mymaze[0][0].setDistance(0);
mymaze[0][0].setStartPoint(true);
//set up as(MAZE_WIDTH-1,MAZE_HEIGHT-1) as exit point
mymaze[MAZE_HEIGHT-1][MAZE_WIDTH-1].setData(0);
mymaze[MAZE_HEIGHT-1][MAZE_WIDTH-1].setDistance(0);
//set maze as below which is wall
/*
*
{ 0, 0, 0, 0, 0},
{-1, 0, -1, 0, -1},
{ 0, 0, -1, -1, -1},
{ 0, -1, 0, 0, 0},
{ 0, 0, 0, -1, 0},
*
*/
mymaze[1][0].setData(-1);
mymaze[3][1].setData(-1);
mymaze[1][2].setData(-1);
mymaze[2][2].setData(-1);
mymaze[2][3].setData(-1);
mymaze[4][3].setData(-1);
mymaze[1][4].setData(-1);
mymaze[2][4].setData(-1);
//process now
Queue<MazeShow.MazeNode> mazequeue = new LinkedList<MazeShow.MazeNode>();
//push the entrance point as start point
mazequeue.offer(mymaze[0][0]);
MazeShow.MazeNode pnode=null;
int pcolumn=0, prow=0;
MazeShow.MazeNode currentNode=null;
/*
* 1、add()和offer()区别:
add()和offer()都是向队列中添加一个元素。一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,调用 add() 方法就会抛出一个 unchecked 异常,而调用 offer() 方法会返回 false。因此就可以在程序中进行有效的判断!
2、poll()和remove()区别:
remove() 和 poll() 方法都是从队列中删除第一个元素。如果队列元素为空,调用remove() 的行为与 Collection 接口的版本相似会抛出异常,但是新的 poll() 方法在用空集合调用时只是返回 null。因此新的方法更适合容易出现异常条件的情况。
* *
*/
while(!mazequeue.isEmpty()) {
//get the FIFO node from queue;
currentNode=null;
pnode=mazequeue.poll();
/*依次把上 下 左 右 四个相邻的节点的距离更新后,然后把这些节点送进队列里,处理这4个方向节点完毕后,
* 再从队列头取一个节点,再次重上上面的处理,直到处理到完所有能从起点开始的节点。
* */
for(int turn=0;turn<4;turn++) {
switch(turn) {
case 0: //left
if(pnode.getColumn()>0) {
pcolumn=pnode.getColumn()-1;
prow=pnode.getRow();
currentNode=mymaze[prow][pcolumn];
}
else
currentNode=null;
break;
case 1: //down
if(pnode.getRow()<(MAZE_HEIGHT-1)) {
// not the lowest node
pcolumn=pnode.getColumn();
prow=pnode.getRow()+1;
currentNode=mymaze[prow][pcolumn];
}
else
currentNode=null;
break;
case 2: //right
if(pnode.getColumn()<(MAZE_WIDTH-1)) {
// not the most right node
pcolumn=pnode.getColumn()+1;
prow=pnode.getRow();
currentNode=mymaze[prow][pcolumn];
}
else
currentNode=null;
break;
case 3: //up
if(pnode.getRow()>0) {
pcolumn=pnode.getColumn();
prow=pnode.getRow()-1;
currentNode=mymaze[prow][pcolumn];
} else
currentNode=null;
break;
default:
System.out.println("error, shall not reach here");
return -1;
}
if(currentNode==null)
continue;
//skip the wall node
if(currentNode.getData()== -1)
continue;
distance=currentNode.getDistance();
if(distance==0 && (!currentNode.getStartPoint())){
//this is node not processed yet and it is not startpoint
currentNode.setDistance(pnode.getDistance()+1);
mazequeue.offer(currentNode);
//set currentNode.preNode as pnode.
currentNode.setprevNode(pnode);
}
else { if(distance>(pnode.getDistance()+1)){
//update to make it shorter
currentNode.setDistance(pnode.getDistance()+1);
mazequeue.offer(currentNode);
currentNode.setprevNode(pnode);
}
else { //if distance <= (pnode.getDistance()+1
//nothing to do
}
}
}
}
System.out.println("the shortest path is: "+mymaze[MAZE_HEIGHT-1][MAZE_WIDTH-1].getDistance());
if(mymaze[MAZE_HEIGHT-1][MAZE_WIDTH-1].getDistance() >0 ) {
//print out the shortest path from the start point.
MazeNode node=mymaze[MAZE_HEIGHT-1][MAZE_WIDTH-1].getprevNode();
Stack<MazeNode> stack=new Stack<>();
stack.push(mymaze[MAZE_HEIGHT-1][MAZE_WIDTH-1]);
if(node != null) {
stack.push(node);
node=node.getprevNode();
}
while(node != null) {
stack.push(node);
node=node.getprevNode();
}
node=stack.pop();
if(null != node)
System.out.print("("+node.getRow()+","+node.getColumn()+")");
while(!stack.isEmpty()) {
node=stack.pop();
System.out.print("->("+node.getRow()+","+node.getColumn()+")");
}
}
return (distance= mymaze[MAZE_HEIGHT-1][MAZE_WIDTH-1].getDistance());
}
}
输出结果是:
the shortest path is: 12
(0,0)->(0,1)->(1,1)->(2,1)->(2,0)->(3,0)->(4,0)->(4,1)->(4,2)->(3,2)->(3,3)->(3,4)->(4,4)